﻿
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System;

namespace PluginDB
{
    public class CommonRepository
    {
        private readonly Repository<Category, PluginDbEntities> _categoryRepository;
        private readonly Repository<Source, PluginDbEntities> _sourceRepository;
        private readonly Repository<Site, PluginDbEntities> _siteRepository;
        private readonly Repository<User, PluginDbEntities> _userRepository;
        private readonly Repository<FileHostingCooky, PluginDbEntities> _cookieRepository;
        private readonly Repository<Job, PluginDbEntities> _jobRepository;
        private readonly Repository<JobsType, PluginDbEntities> _jobsTypeRepository;

        public CommonRepository(PluginDbEntities pluginDbEntities)
        {
            _categoryRepository = new Repository<Category, PluginDbEntities>(pluginDbEntities);
            _sourceRepository = new Repository<Source, PluginDbEntities>(pluginDbEntities);
            _siteRepository = new Repository<Site, PluginDbEntities>(pluginDbEntities);
            _userRepository = new Repository<User, PluginDbEntities>(pluginDbEntities);
            _cookieRepository = new Repository<FileHostingCooky, PluginDbEntities>(pluginDbEntities);
            _jobRepository = new Repository<Job, PluginDbEntities>(pluginDbEntities);
            _jobsTypeRepository = new Repository<JobsType, PluginDbEntities>(pluginDbEntities);
        }

        #region Create Methods
        public Site CreateSite(string name)
        {
            var site = new Site
            {
                SiteName = name,
                SiteURL = name
            };

            _siteRepository.Add(site);
            return site;
        }

        public Source CreateSource(string sourceName)
        {
            var source = new Source { SourceName = sourceName };

            _sourceRepository.Add(source);
            return source;
        }

        public Category CreateCategory(string name)
        {
            var category = new Category
            {
                CategoryName = name
            };

            _categoryRepository.Add(category);
            return category;
        }
        #endregion

        #region Plugin Videos Methods
        internal Source GetSource(string name)
        {
            return _sourceRepository.DoQuery(
                new Specification<Source>(s => s.SourceName.ToLower().Equals(name.ToLower()))).FirstOrDefault() ??
                         CreateSource(name);
        }

        internal Category GetCategory(string name)
        {
            return _categoryRepository.DoQuery(
                new Specification<Category>(a => a.CategoryName.ToLower().Equals(name.ToLower()))).FirstOrDefault() ??
                           CreateCategory(name);
        }

        internal Site GetSite(string name)
        {
            return _siteRepository.DoQuery(
                new Specification<Site>(s => s.SiteName.Equals(name))).FirstOrDefault() ?? CreateSite(name);
        }

        internal List<Category> GetCategoriesList(int page, int pageSize)
        {
            var categories = _categoryRepository.SelectAll().Page(page, pageSize).ToList();
            return categories;
        }

        internal List<Site> GetSitesNames(int page, int pageSize)
        {
            return _siteRepository.SelectAll().Page(page, pageSize).ToList();
        }

        internal FileHostingCooky GetCookieCode(string sourceName)
        {
            var source = GetSource(sourceName);
            var cookie = _cookieRepository
                            .Session
                            .FileHostingCookies
                            .Where(c => c.Source.SourceID == source.SourceID)
                            .FirstOrDefault();

            return cookie;
        }

        internal void InsertNewCookieCode(string cookieCode, DateTime expiredDate, string sourceName)
        {
            var cookies = _cookieRepository
                            .Session
                            .FileHostingCookies
                            .Where(c => c.Source.SourceName == sourceName)
                            .ToList();

            foreach (var fileHostingCooky in cookies)
            {
                _cookieRepository.Delete(fileHostingCooky);
            }

            //if (cookie != null && cookie.CookieValue != cookieCode)
            //{
            //    cookie.CookieValue = cookieCode;
            //    _cookieRepository.AddOrAttach(cookie);
            //}


            var cookie = new FileHostingCooky
                               {
                                   CookieValue = cookieCode,
                                   Source = GetSource(sourceName),
                                   Expired = expiredDate
                               };
            _cookieRepository.Add(cookie);

            if (cookie.EntityState == EntityState.Unchanged) return;

            _cookieRepository.Save();
        }

        internal User GetUser(long userID)
        {
            return _userRepository.DoQuery(
                new Specification<User>(u => u.UserID.Equals(userID))).FirstOrDefault();
        }

        internal User GetUser(string userName, string password)
        {
            return _userRepository.DoQuery(
                new Specification<User>(
                    u => u.UserName.ToLower().Equals(userName.ToLower()) &&
                    u.Password.ToLower().Equals(password.ToLower())))
                    .FirstOrDefault();
        }

        internal User GetUser(string userGuid)
        {
            return _userRepository.DoQuery(
                new Specification<User>(u => u.UserUniqeGuid.ToLower().Equals(userGuid.ToLower())))
                .FirstOrDefault();
        }

        internal bool IsUserExists(long userId)
        {
            return _userRepository.Session.Users.Any(u => u.UserID == userId);
        }

        internal bool IsUserExists(string userName, string password)
        {
            var user = _userRepository.Session.Users.Any(
                        u => u.UserName.ToLower().Equals(userName.ToLower()) &&
                            u.Password.ToLower().Equals(password.ToLower()));

            return user;
        }

        internal bool IsUserNameExists(string userName)
        {
            return _userRepository.Session.Users.Any(u => u.UserName.ToLower().Equals(userName.ToLower()));
        }

        internal bool IsUserExists(string userUniqeGuid)
        {
            return _userRepository.Session.Users.Any(u => u.UserUniqeGuid == userUniqeGuid);
        }

        internal void AddSeriesToUser(long userId, Series series)
        {
            var user = _userRepository.Session.Users.Where(u => u.UserID == userId).FirstOrDefault();
            if (user == null) throw new Exception("User not found");
            user.Series.Add(series);
            _userRepository.Save();
        }

        internal void AddVideoToUser(long userId, Video video)
        {
            var user = _userRepository.Session.Users.Where(u => u.UserID == userId).FirstOrDefault();
            if (user == null) throw new Exception("User not found");
            user.Videos.Add(video);
            _userRepository.Save();
        }

        internal void RemoveSeriesForUser(long userId, Series series)
        {
            var user = _userRepository.Session.Users.Include("Series").Where(u => u.UserID == userId).FirstOrDefault();
            if (user == null) throw new Exception("User not found");
            user.Series.Remove(series);
            _userRepository.Save();
        }

        internal void RemoveVideoForUser(long userId, Video video)
        {
            var user = _userRepository.Session.Users.Include("Videos").Where(u => u.UserID == userId).FirstOrDefault();
            if (user == null) throw new Exception("User not found");
            user.Videos.Remove(video);
            _userRepository.Save();
        }

        internal User CreateUser(string userName, string password)
        {
            var user = new User
            {
                CreateTime = DateTime.Now,
                IsActive = false,
                Password = password,
                UserName = userName,
                UserUniqeGuid = Guid.NewGuid().ToString(),
            };

            _userRepository.Add(user);
            _userRepository.Save();
            return user;
        }

        internal bool IsUserSeriesExists(long userId, long seriesId)
        {
            var user = _userRepository.Session.Users.Where(u => u.UserID == userId).FirstOrDefault();
            if (user == null) throw new Exception("User not found");
            return user.Series.Any(s => s.SeriesID == seriesId);
        }

        internal bool IsUserVideoExists(long userId, long videoId)
        {
            var user = _userRepository.Session.Users.Where(u => u.UserID == userId).FirstOrDefault();
            if (user == null) throw new Exception("User not found");
            return user.Videos.Any(s => s.VideoID == videoId);
        } 
        #endregion

        #region Jobs Methods

        internal List<Job> GetSiteScrapperJobs()
        {
            return _jobRepository.Session.Jobs.Where(j=>j.JobsType.JobTypeID == 1).ToList();
        }

        internal void UpdateJobTypeRunTime(long jobTypeID, DateTime runTime)
        {
            var job = _jobRepository.Session.JobsTypes.Where(j => j.JobTypeID == jobTypeID).FirstOrDefault();
            if (job == null) throw new Exception(string.Format("JobType {0} Not found. Update aborted!!!", jobTypeID));

            job.LastRunTime = runTime;
            _jobsTypeRepository.Save();
        }

        internal void UpdateJobRunTime(long jobId, DateTime runTime)
        {
            var job = _jobRepository.Session.Jobs.Where(j => j.JobID == jobId).FirstOrDefault();
            if (job == null) throw new Exception(string.Format("Job {0} Not found. Update aborted!!!", jobId));

            job.LastRunTime = runTime;
            _jobRepository.Save();
        }

        internal List<JobsType> GetJobsTypesList()
        {
            return _jobRepository.Session.JobsTypes.ToList();
        }

        internal Job CreateJob(string jobName, string jobsTypeName)
        {
            var job = new Job
            {
                Active = true,
                JobsType = GetJobType(jobsTypeName),
                Name = jobName,
                LastRunTime = DateTime.Now.AddMinutes(-1)
            };

            _jobRepository.Add(job);
            _jobRepository.Save();
            return job;
        }

        internal Job GetJob(string jobName)
        {
            return _jobRepository.Session.Jobs.FirstOrDefault(j => j.Name.Equals(jobName));
        }

        internal JobsType CreateJobType(string name, double runTimeIntervalInHOUR)
        {
            var jobsType = new JobsType
            {
                Name = name,
                RunTimeIntervalInHOUR = runTimeIntervalInHOUR,
                LastRunTime = DateTime.Now
            };

            _jobsTypeRepository.Add(jobsType);
            return jobsType;
        }

        internal JobsType GetJobType(string name)
        {
            return _jobsTypeRepository.DoQuery(
                new Specification<JobsType>(a => a.Name.ToLower().Equals(name.ToLower()))).FirstOrDefault();
        }

        
        #endregion

        internal bool ActivateUser(string userGuid)
        {
            var user = _userRepository.Session.Users.FirstOrDefault(u => u.UserUniqeGuid == userGuid);
            if (user == null) throw new Exception("The user doesnt exists");

            try
            {
                if (user.IsActive) return false;

                user.IsActive = true;
                
                _userRepository.Save();
                return true;
            }
            catch(Exception)
            {
                return false;
            }
        }

        internal bool IsSiteExists(string siteName)
        {
            var site = _siteRepository.Session.Sites.FirstOrDefault(s => s.SiteName.Equals(siteName));
            return site != null;
        }
    }
}
